// author: bovine
// reference: http://www.solosnake.com>www.solosnake.com

#ifndef _ASE_H
#define _ASE_H

#include <string>
#include <vector>
#include <fstream>
#include <assert.h>  // windows platform dependent

#include "mtxlib.h"

//
// Flags detailing the information that is in the material
//
#define MNAME           (0x00000001)
#define MCLASS			(0x00000002)
#define MAMBIENT        (0x00000004)
#define MDIFFUSE        (0x00000008)
#define MSPECULAR       (0x00000010)
#define MSHINE          (0x00000020)
#define MSHINESTRENGTH  (0x00000040)
#define MTRANSPARENCY   (0x00000080)
#define MWIRESIZE       (0x00000100)
#define MSUBMATERIAL	(0x00000200)
#define MSHADING        (0x00000400)
#define MXPFALLOF		(0x00000800)
#define MSELFILLUM		(0x00001000)
#define MFALLOF			(0x00002000)
#define MXPTYPE			(0x00004000)
#define MMAPDIFFUSE     (0x00008000)
#define MMAPAMBIENT		(0x00010000)
#define MMAPOPACITY		(0x00020000)	
#define MMAPSPECULAR	(0x00040000)
#define MMAPBUMP		(0x00080000)
#define MMAPREFLECT		(0x00100000)
#define MTWOSIDED		(0x00200000)

#define MJPEGENABLE     (0x00400000)  // enable JPEG load



//
// Flags detailing what is in map
//
#define MAPNAME			(0x00000001)
#define MAPCLASS        (0x00000002)
#define MAPSUBNO		(0x00000004)
#define MAPAMOUNT		(0x00000008)
#define MAPBITMAP		(0x00000010)
#define MAPTYPE			(0x00000020)
#define MAPUOFFSET		(0x00000040)
#define MAPVOFFSET		(0x00000080)
#define MAPUTILING		(0x00000100)
#define MAPVTILING		(0x00000200)
#define MAPANGLE		(0x00000400)
#define MAPBLUR			(0x00000800)
#define MAPBLUROFFSET   (0x00001000)
#define MAPNOISEAMT		(0x00002000)
#define MAPNOISESIZE	(0x00004000)
#define MAPNOISELEVLE	(0x00008000)
#define MAPNOISEPHASE	(0x00010000)
#define MAPBITMAPFILTER (0x00020000)

//
// Flags detailing what is in the mesh
//
#define MESHTIMEVALUE   (0x00000001)
#define MESHTVERTEX     (0x00000002)
#define MESHCVERTEX		(0x00000004)
#define MESHNORMAL      (0x00000008)

namespace ASE {
	typedef enum { 
		NOFACES              = 1,
		NOVERTICES           = 2,
		NOTEXTUREVERTICES    = 3,
		NOTEXTUREFACES       = 4,
		NOMESHNORMALS        = 5,
		INVALIDMESHNORMALS   = 6,
		INVALIDMESHVNORMAL   = 7,
		MESHERROR            = 8,
		NOMATERIALREF        = 9,
		INVALIDMATERIAL      = 10,
		INVALIDASEFILE       = 11,
		CANNOTOPENFILE       = 12,
		UNKNOWNERROR         = 65535 /* 0xFFFF */
	} ERRORCODE;

	// typedef Vector3d<double> GeomPoint;
	typedef vector3 GeomPoint;

	class Map;

	// struct to represent the node data in a gemotry object
	class GeomObjectNode 
	{
	public:
		GeomObjectNode():iErrorNo(0),TMRotAngle(0.0),TMScaleAxisAng(0.0),node_name("")
		{
			inherit_pos[0] = inherit_pos[1] = inherit_pos[2] = 0.0f;
			inherit_rot[0] = inherit_rot[1] = inherit_rot[2] = 0.0f;
			inherit_scl[0] = inherit_scl[1] = inherit_scl[2] = 1.0f;
			TMRow0[0] = TMRow0[1] = TMRow0[2] = 0.0f;
			TMRow1[0] = TMRow1[1] = TMRow1[2] = 0.0f;
			TMRow2[0] = TMRow2[1] = TMRow2[2] = 0.0f;
			TMPos[0]  = TMPos[1]  = TMPos[2]  = 0.0f;
			TMRotAxis[0] = TMRotAxis[1] = TMRotAxis[2] = 0.0f;
			TMScale[0] = TMScale[1] = TMScale[2] = 1.0f;
			TMScaleAxis[0] = TMScaleAxis[1] = TMScaleAxis[2] = 0.0f;
		}

		bool read(std::ifstream&);
		int  GetError(void) const throw(){return iErrorNo;};

		std::string    node_name;          // Name of Node.
		float          inherit_pos[3];     // Values of INHERIT_POS
		float          inherit_rot[3];     // Values of INHERIT_ROT
		float          inherit_scl[3];     // Values of INHERIT_SCL
		float          TMRow0[3];         // Values of TM_ROW0
		float          TMRow1[3];         // Values of TM_ROW1
		float          TMRow2[3];         // Values of TM_ROW2
		float          TMPos[3];          // Values of TM_POS
		float          TMRotAxis[3];      // Values of TM_ROTAXIS
		float          TMRotAngle;        // Value of  TM_ROTANGLE
		float          TMScale[3];        // Values of TM_SCALE
		float          TMScaleAxis[3];    // Values of TM_SCALEAXIS
		float          TMScaleAxisAng;    // Value of TM_SCALEAXISANG
	private:
		int             iErrorNo;           // Error code. Zero if no error occurs.
	};

	//
	//   Class to represent the mesh data
	//   TODO: HANDLE MESH_MAPPINGCHANNEL HERE
	//   TODO: HANDLE COLOR VERTEX HERE
	class GeomObjectMesh {
	public:
		GeomObjectMesh()
			:time_value(0.0f),face_count(0),vertex_count(0),
			is_motion_blur(false),is_cast_shadow(false),is_recv_shadow(false),
			material_ref(-1),iErrorNo(0)
		{};

		bool read(std::ifstream&);

		int  GetError(void)const throw()
		{return iErrorNo;};

		unsigned long GetFlags(void)const throw()
		{return ulFlags;};


		float                   time_value;             // Value of the TIMEVALUE.
		int                     face_count;             // Number of faces in mesh. Repeated data but for ease of use.
		int                     vertex_count;           // Number of vertexes held. Repeated data for ease of use.
		std::vector<GeomPoint>  vertex_list;            // The points in the mesh vertex list in order.
		std::vector<int>        face_list;              // The indexes into the mesh vertex list that define the 3D object.
		std::vector<int>        face_edge_list;         // The data set of 3-tuples with the indexes, whose purpose I am not sure.
		std::vector<int>        face_list_materialID;   // Maybe boolean control edge's visibility.
		std::vector<GeomPoint>  tvert_list;             // Array of texture coordinates. The z-value is rarely used and should be zero.
		std::vector<int>        tface_list;             // Array of indexes into the texture coords, 3 per face.
		std::vector<GeomPoint>  face_normals;           // Array of normals to faces.
		std::vector<GeomPoint>  vertex_normals;         // Array of normals to each vertex in a face. There are thus 3x the number as in mesh_facenormals.
		bool                    is_motion_blur;         // Value of PROP_MOTIONBLUR, presumed to be the property of the mesh concerning motion blur. In the files these seem to be only zero or one.
		bool                    is_cast_shadow;         // Value of PROP_CASTSHADOW, presumed to be the property of the mesh concerning motion blur. In the files these seem to be only zero or one.
		bool                    is_recv_shadow;         // Value of PROP_RECVSHADOW, presumed to be the property of the mesh concerning motion blur. In the files these seem to be only zero or one.
		int                     material_ref;           // Value of MATERIAL_REF, which refers to which material in the list to use.    

	private:
		int                     iErrorNo;               // Error code. Should be zero for no errors.
		unsigned long           ulFlags;                // Flags about the contents of mesh.
	};

	//   Struct to represent a single *GEOMOBJECT in an ASE file
	class GeomObject {
	public:
		GeomObjectNode  node;
		GeomObjectMesh  mesh;

		bool read(std::ifstream&);
		int  GetError(void)const throw(){return iErrorNo;};

	private:
		int         iErrorNo;           // Error code. Zero if no error occurs.
	};

	// class corresponds to MAP_DIFFUSE, MAP_AMBIENT, MAP_OPACITY, MAP_SPECULAR sections and so on.
	class Map
	{
	public:
		Map() : map_name(""),
			map_class("Bitmap"),
			map_subno(1),
			map_amount(1.0f),
			bitmap("NO_BITMAP"),
			map_type("Screen"),
			uvw_u_offset(0.0f), uvw_v_offset(0.0f), uvw_u_tiling(1.0f), uvw_v_tiling(1.0f),
			uvw_angle(0.0f), uvw_blur(1.0f), uvw_blur_offset(0.0f),
			uvw_noise_amt(1.0f), uvw_noise_size(1.0f), uvw_noise_level(1), uvw_noise_phase(0.0f),
			bitmap_filter("Pyramidal")
		{}

		bool read(std::ifstream&, const char*);

		int  GetError(void)const throw()
		{return iErrorNo;};

		unsigned long GetFlags(void)const throw()
		{return ulFlags;};

		std::string				map_name;
		std::string				map_class;
		int     				map_subno;
		float                   map_amount;
		std::string				bitmap;
		std::string				map_type;

		float               uvw_u_offset;           // Value of UVW_U_OFFSET.
		float               uvw_v_offset;           // Value of UVW_V_OFFSET.
		float               uvw_u_tiling;           // Value of UVW_U_TILING.
		float               uvw_v_tiling;           // Value of UVW_V_TILING.
		float               uvw_angle;              // Value of UVW_ANGLE.
		float               uvw_blur;               // Value of UVW_BLUR.
		float               uvw_blur_offset;        // Value of UVW_BLUR_OFFSET.
		float				uvw_noise_amt;          // Value of UVW_NOISE_AMT.
		float				uvw_noise_size;         // Value of UVW_NOISE_SIZE.
		int                 uvw_noise_level;        // Value of UVW_NOISE_LEVEL.
		float               uvw_noise_phase;        // Value of UVW_NOISE_PHASE.

		std::string         bitmap_filter;          // Bitmap Filter

		std::string         path;                   // the path of ase file

	private:
		int                 iErrorNo;               // Error code. Zero if no error occurs.
		unsigned long       ulFlags;                // Flags
	};

	//   Class to represent a material reference in the ASE file.
	//   TODO: HANDLE SUB_MATERIAL HERE.
	class Material {
	public:
		Material():material_name(""),
			material_class("Standard"),
			shine(0.0f), 
			shine_strength(0.0f), 
			transparency(0.0f),
			wire_size(1.0f),
			shading("Phong"),
			xp_fallof(0.0f),
			selfillum(0.0f),
			fallof("In"),
			xp_type("Filter"),
			num_of_sub_materials(0),
			sub_materials(NULL),
			ulFlags(0)
		{
			ambient[0]  = ambient[1]  = ambient[2]  = 0.2f;   // OpenGL default.
			diffuse[0]  = diffuse[1]  = diffuse[2]  = 0.8f;   // OpenGL default.
			specular[0] = specular[1] = specular[2] = 0.0f;   // OpenGL default.
		};

		~Material() { clear(); }

		Material(const Material& rhs) {copy_from(rhs);}
		Material& operator=(const Material& rhs)
		{
			if (&rhs == this) return *this;
			clear();
			copy_from(rhs);
			return *this;
		}

		bool read(std::ifstream&, const char*);

		int  GetError(void)const throw() {return iErrorNo;};

		unsigned long GetFlags(void)const throw() {return ulFlags;};

		std::string         material_name;         // Value of MATERIAL_NAME.
		std::string		    material_class;		   // Value of MATERIAL_CLASS
		float               ambient[3];            // Values of MATERIAL_AMBIENT.
		float               diffuse[3];            // Values of MATERIAL_DIFFUSE.
		float               specular[3];           // Values of MATERIAL_SPECULAR.
		float               shine;                 // Value of MATERIAL_SHINE.
		float               shine_strength;        // Value of MATERIAL_SHINESTRENGTH.
		float               transparency;          // Value of MATERIAL_TRANSPARENCY.
		float               wire_size;             // Value of MATERIAL_WIRESIZE.
		std::string         shading;               // Value of MATERIAL_SHADING.
		float			    xp_fallof;			   // Value of MATERIAL_XP_FALLOF
		float               selfillum;			   // Value of MATERIAL_SELFILLUM
		std::string         fallof;                // Value of MATERIAL_FALLOF
		std::string         xp_type;			   // Value of MATERIAL_XP_TYPE

		Map				    map_diffuse;		   // Value of MAP_DIFFUSE
		Map                 map_reflect;		   // Value of MAP_REFLECT
		Map                 map_ambient;           // Value of MAP_AMBIENT
		Map                 map_opacity;		   // Value of MAP_OPACITY
		Map                 map_specular;          // Value of MAP_SPECULAR
		Map                 map_bump;              // Value of MAP_BUMP

		int					num_of_sub_materials;  // Number of sub materials
		std::vector<Material*>	sub_materials;	   // sub materials

		std::string         path;                   // the path of ase file

	private:
		int                 iErrorNo;               // Error code. Zero if no error occurs.
		unsigned long       ulFlags;                // Flags
		void				copy_from(const Material&);
		void				clear();
	};

	// Class to hold details of a single ase file.
	class   ASEFile {
	public:
		ASEFile();

		int read(const char*);
		int read(std::ifstream&);

		int GetWarnings(void)const throw()
		{return iWarning;};

		int GetFaceCount(void)const throw();

		std::vector<ASE::Material>::size_type GetMaterialCount(void)const throw()
		{return materials.size();};

		std::vector<ASE::GeomObject>::size_type GetGeomObjectCount(void)const throw()
		{return geomobjects.size();};

		std::string                 scenename;  // the read() funciton's parameters stored here.
		float                       background_static[3];
		float                       ambient_static[3];
		std::vector<Material>       materials;
		std::vector<GeomObject>     geomobjects;

		std::string					path; // the path of this ase file

	private:
		int                         iWarning;
	};

} // End of namespace ASE

#endif // _ASE_H